package gnss

import (
	"bytes"
	"dgo/goutils/utils"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/net/proxy"
	"io"
	"net"
	"strings"
	"sync/atomic"
)

type NtripClientCltSimple struct {
	disFlag              int32
	DisReason            string
	conn                 net.Conn
	casteraddr           string
	DialProxyAddr        string
	FirstRecv            string
	RequestHead          string
	AuthState            int8 // 0: 未获取认证数据, 1:认证成功, -1:认证失败, 执行ReadFirst()时操作
	OnBuildRequestHeadFn func(hb *utils.BytesBuilder)

	TagIntV int
}

func NewNtripClientCltSimple(casteraddr string) *NtripClientCltSimple {
	rval := &NtripClientCltSimple{
		casteraddr: casteraddr,
	}
	return rval
}

func NewNtripClientCltSimple0() *NtripClientCltSimple {
	rval := &NtripClientCltSimple{}
	return rval
}

func (this *NtripClientCltSimple) Close() error {
	conn := this.conn
	if conn == nil {
		return nil
	}
	this.conn = nil
	return conn.Close()
}

func (this *NtripClientCltSimple) ConfigAddr(addr string) bool {
	if this.casteraddr != addr {
		this.casteraddr = addr
		return true
	}
	return false
}

func (this *NtripClientCltSimple) RequestDis(reason string) {
	if atomic.CompareAndSwapInt32(&this.disFlag, 0, 1) {
		conn := this.conn
		if conn != nil {
			conn.Close()
		}
		this.DisReason = fmt.Sprintf("[%s]%s", utils.NowString(), reason)
		// this.conn = nil 不能， 避免异步调用时， 关闭了，导致读取写入时出现异常
	}
}

/*
*

	发送请求信息
*/
func (this *NtripClientCltSimple) WriteRequestBuf(user, pass, mountpoint, useragent string) error {
	conn := this.conn
	if conn == nil {
		return errors.New("连接尚未建立!")
	}
	var sb utils.BytesBuilder
	sb.Appendf("GET /%s HTTP/1.0\r\n", mountpoint)
	if len(useragent) > 0 {
		sb.Appendf("User-Agent:NTRIP %s\r\n", useragent)
	}
	if len(user) > 0 {
		str := user + ":" + pass
		authstr := base64.StdEncoding.EncodeToString([]byte(str))
		sb.Appendf("Authorization: Basic %s\r\n", authstr)
	}
	if this.OnBuildRequestHeadFn != nil {
		this.OnBuildRequestHeadFn(&sb)
	}
	sb.AppendStr("\r\n")
	str := sb.String()
	this.RequestHead = str
	_, err := conn.Write([]byte(str))
	return err
}

func (this *NtripClientCltSimple) RequestData(user, pass, mountpoint, useragent string) error {
	return this.WriteRequestBuf(user, pass, mountpoint, useragent)
}

func (this *NtripClientCltSimple) ReportNMEA(nmea string) error {
	conn := this.conn
	if conn == nil {
		return errors.New("连接尚未建立!")
	}
	if !strings.HasSuffix(nmea, "\r\n") {
		nmea += "\r\n"
	}
	_, err := conn.Write([]byte(nmea))
	return err
}

/*
**

	读取数据直到接收到数据
	  过滤掉认证信息ICY 200 OK
	  如果认证失败则返回err
*/
func (this *NtripClientCltSimple) ReadUntilData(b []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, errors.New("连接尚未建立!")
	}

	auth_cnt := 0
	buf := make([]byte, 512)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			return n, err
		}

		str := string(buf[:n])
		if strings.HasPrefix(str, "ICY 200 OK") {
			auth_cnt++
			continue
		} else if strings.Contains(str, "401 Unauthorized") {
			if auth_cnt == 0 {
				return 0, errors.New(str)
			} else {
				continue
			}
		} else {
			if b != nil {
				copy(b, buf[:n])
			}
			return n, nil
		}
	}
}

func (this *NtripClientCltSimple) ReadFirstEx(cache *utils.BytesBuilder) (first string, err error) {
	r := this.conn
	if r == nil {
		return "", errors.New("连接尚未建立!")
	}
	this.AuthState, first, err = NtripCliReadFirst(r, cache)
	return
}

/*
**

	读取数据直到接收到数据
	  过滤掉认证信息ICY 200 OK
	  如果认证失败则返回err
*/
func (this *NtripClientCltSimple) ReadFirst() (buf []byte, err error) {
	conn := this.conn
	if conn == nil {
		return nil, errors.New("连接尚未建立!")
	}

	newBuf := make([]byte, 512)

	n, err := conn.Read(newBuf)
	if err != nil {
		return nil, err
	}

	icy200Buf := []byte("ICY 200 OK")
	lineBreakBuf := []byte("\r\n")
	unauthBuf := []byte("401 Unauthorized")
	sourcetable := []byte("SOURCETABLE 200 OK")

	newBuf = newBuf[:n]
	trimBuf := make([]byte, 0, 512)

	if bytes.Index(newBuf, icy200Buf) > -1 {
		this.AuthState = 1
		newBuf = bytes.TrimPrefix(newBuf, icy200Buf)
		trimBuf = append(trimBuf, icy200Buf...)
		for {
			if bytes.HasPrefix(newBuf, lineBreakBuf) {
				newBuf = newBuf[2:]
				trimBuf = append(trimBuf, lineBreakBuf...)
			} else {
				break
			}
		}
		this.FirstRecv = string(trimBuf)
		return newBuf, nil
	}

	if bytes.Index(newBuf, sourcetable) > -1 {
		this.AuthState = -1
		this.FirstRecv = string(newBuf[:n])
		return newBuf, fmt.Errorf("源节点不存在")
	}

	if bytes.Index(newBuf, unauthBuf) > -1 {
		this.AuthState = -1
		this.FirstRecv = string(newBuf[:n])
		return newBuf, errors.New(this.FirstRecv)
	}

	if bytes.Index(newBuf, []byte("ERROR")) == 0 {
		this.AuthState = -1
		this.FirstRecv = string(newBuf[:n])
		return newBuf, errors.New(this.FirstRecv)
	}

	// 直接是数据
	this.AuthState = 1
	return newBuf, nil
}

func (this *NtripClientCltSimple) ReadEx(b []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, errors.New("连接尚未建立!")
	}
	return conn.Read(b)

}

func (this *NtripClientCltSimple) WriteEx(b []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, errors.New("连接尚未建立!")
	}
	return conn.Write(b)
}

func (this *NtripClientCltSimple) Read(b []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, io.ErrClosedPipe
	}
	return conn.Read(b)
}

func (this *NtripClientCltSimple) Write(b []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, io.ErrClosedPipe
	}
	return conn.Write(b)
}

func (this *NtripClientCltSimple) Open() error {
	proxyaddr := this.DialProxyAddr

	if len(proxyaddr) > 0 {
		d, err := proxy.SOCKS5("tcp", proxyaddr, nil, proxy.Direct)
		if err != nil {
			return err
		}
		conn, err := d.Dial("tcp", this.casteraddr)
		if err != nil {
			return err
		}
		this.conn = conn
	} else {
		conn, err := net.Dial("tcp", this.casteraddr)
		if err != nil {
			return err
		}
		this.conn = conn
	}

	this.AuthState = 0
	this.disFlag = 0
	return nil
}

func (this *NtripClientCltSimple) Connected() bool {
	return this.conn != nil && this.disFlag == 0
}

func (this *NtripClientCltSimple) CheckConnect() error {
	if !this.Connected() {
		return this.Open()
	}
	return nil
}
